package com.dps.metrics.processor;

import com.dps.metrics.metadata.MetricsMetadata;
import com.dps.metrics.factory.GlobalMetricsFactory;

import java.util.*;

public class CustomizedMetricsProcessor extends AbstractMetricsProcessor {
    private static final String PREFIX = "customized.";
    private static final String SIMPLE_FULL_PREFIX = "fusion.customized.";
    private Map<MetricsMetadata.Metrics, MetricsMetadata> metricsMap;

    private CustomizedMetricsProcessor() {
    }

    public static CustomizedMetricsProcessor.CustomizedMetricsProcessorBuilder builder(String customPrefix) {
        CustomizedMetricsProcessor.CustomizedMetricsProcessorBuilder builder = new CustomizedMetricsProcessor.CustomizedMetricsProcessorBuilder(customPrefix);
        Map<MetricsMetadata.Metrics, MetricsMetadata> metricsMap = new EnumMap(MetricsMetadata.Metrics.class);
        metricsMap.put(MetricsMetadata.Metrics.IN_PROCESS_COUNT, MetricsMetadata.buildFrom("customized." + customPrefix, MetricsMetadata.Metrics.IN_PROCESS_COUNT));
        metricsMap.put(MetricsMetadata.Metrics.REQUEST_COUNT, MetricsMetadata.buildFrom("customized." + customPrefix, MetricsMetadata.Metrics.REQUEST_COUNT));
        metricsMap.put(MetricsMetadata.Metrics.RESPONSE_TIME, MetricsMetadata.buildFrom("customized." + customPrefix, MetricsMetadata.Metrics.RESPONSE_TIME));
        builder.metricsMap = metricsMap;
        return builder;
    }

    public void record(MetricsMetadata metricsMetadata) {
        this.record(metricsMetadata, Collections.emptyMap());
    }

    public void record(MetricsMetadata metricsMetadata, Map<String, String> tags) {
        throw new UnsupportedOperationException("Record metrics method");
    }

    public static void recordCount(String key, String help, long amount, String... tagKeyTagValue) {
        Map<String, String> tags = validateTagValue(tagKeyTagValue);
        GlobalMetricsFactory.getGlobal().createCounter("fusion.customized." + key, tags, help).inc(amount);
    }

    public static void recordOneCount(String key, String help, String... tagKeyTagValue) {
        recordCount(key, help, 1L, tagKeyTagValue);
    }

    public static void recordGauge(String key, String help, long amount, String... tagKeyTagValue) {
        Map<String, String> tags = validateTagValue(tagKeyTagValue);
        GlobalMetricsFactory.getGlobal().createGauge("fusion.customized." + key, tags, help).set(amount);
    }

    public static void recordDurationTime(String key, String help, long nanoSeconds, String... tagKeyTagValue) {
        Map<String, String> tags = validateTagValue(tagKeyTagValue);
        GlobalMetricsFactory.getGlobal().createTimer("fusion.customized." + key, tags, help).durationNanoSeconds(nanoSeconds);
    }

    private static Map<String, String> validateTagValue(String[] tagKeyTagValue) {
        if ((tagKeyTagValue.length & 1) == 1) {
            throw new IllegalArgumentException("The key and value of the tags must appear in pairs.");
        } else {
            Map<String, String> tags = new HashMap(tagKeyTagValue.length / 2);

            for (int i = 0; i < tagKeyTagValue.length; ++i) {
                if ((i & 1) == 0) {
                    if (tagKeyTagValue[i] == null || "".equals(tagKeyTagValue[i])) {
                        tagKeyTagValue[i] = "unknown_tag" + i;
                    }

                    tags.put(tagKeyTagValue[i], tagKeyTagValue[i + 1]);
                }
            }

            return tags;
        }
    }

    public void recordStart(Map<String, String> tags) {
        MetricsMetadata inProcessMetrics = (MetricsMetadata) this.metricsMap.get(MetricsMetadata.Metrics.IN_PROCESS_COUNT);
        GlobalMetricsFactory.getGlobal().createGauge(inProcessMetrics.key(), tags, inProcessMetrics.help()).incConc();
    }

    public void recordStart() {
        this.recordStart(this.baseTags);
    }

    public void recordSuccess(Map<String, String> tags) {
        this.recordResponseTime(tags, Boolean.FALSE);
    }

    public void recordSuccess() {
        this.recordResponseTime(Boolean.FALSE);
    }

    public void recordError(Map<String, String> tags) {
        this.recordResponseTime(tags, Boolean.TRUE);
    }

    public void recordError() {
        this.recordResponseTime(Boolean.TRUE);
    }

    private void recordResponseTime(Map<String, String> tags, Boolean error) {
        Map<String, String> map = new HashMap(tags.size() + 1);
        map.putAll(tags);
        map.put("error", error.toString());
        MetricsMetadata metricsMetadata = (MetricsMetadata) this.metricsMap.get(MetricsMetadata.Metrics.RESPONSE_TIME);
        GlobalMetricsFactory.getGlobal().createTimer(metricsMetadata.key(), map, metricsMetadata.help()).durationNanoSeconds(System.nanoTime() - super.startTime);
        MetricsMetadata countMetrics = (MetricsMetadata) this.metricsMap.get(MetricsMetadata.Metrics.REQUEST_COUNT);
        GlobalMetricsFactory.getGlobal().createCounter(countMetrics.key(), map, countMetrics.help()).inc(1L);
    }

    private void recordResponseTime(Boolean error) {
        this.recordResponseTime(this.baseTags, error);
    }

    public void recordFinished(Map<String, String> tags) {
        MetricsMetadata metricsMetadata = (MetricsMetadata) this.metricsMap.get(MetricsMetadata.Metrics.IN_PROCESS_COUNT);
        GlobalMetricsFactory.getGlobal().createGauge(metricsMetadata.key(), tags, metricsMetadata.help()).decConc();
    }

    public void recordFinished() {
        this.recordFinished(this.baseTags);
    }

    public static class CustomizedMetricsProcessorBuilder {
        private String customPrefix;
        private Map<MetricsMetadata.Metrics, MetricsMetadata> metricsMap;
        private Map<String, String> basicTags;

        private CustomizedMetricsProcessorBuilder() {
            this.metricsMap = new HashMap(2);
            this.basicTags = new HashMap(2);
        }

        private CustomizedMetricsProcessorBuilder(String customPrefix) {
            this.metricsMap = new HashMap(2);
            this.basicTags = new HashMap(2);
            if (customPrefix != null && !customPrefix.isEmpty()) {
                this.customPrefix = customPrefix;
            } else {
                throw new IllegalArgumentException("Metrics name prefix is not allow an empty value");
            }
        }

        public CustomizedMetricsProcessor.CustomizedMetricsProcessorBuilder tagsMap(Map<String, String> tags) {
            Map<String, String> unmodifiableMap = Collections.unmodifiableMap(tags);
            if (!unmodifiableMap.isEmpty()) {
                this.basicTags = unmodifiableMap;
            }

            return this;
        }

        public CustomizedMetricsProcessor.CustomizedMetricsProcessorBuilder helpMap(Map<MetricsMetadata.Metrics, String> metricsHelpMap) {
            if (metricsHelpMap != null) {
                Iterator var2 = metricsHelpMap.entrySet().iterator();

                while (var2.hasNext()) {
                    Map.Entry<MetricsMetadata.Metrics, String> metricsStringEntry = (Map.Entry) var2.next();
                    MetricsMetadata.Metrics key = (MetricsMetadata.Metrics) metricsStringEntry.getKey();
                    this.metricsMap.put(key, MetricsMetadata.buildFrom("customized." + this.customPrefix, key, (String) metricsHelpMap.get(key)));
                }
            }

            return this;
        }

        public CustomizedMetricsProcessor build() {
            CustomizedMetricsProcessor reporter = new CustomizedMetricsProcessor();
            reporter.metricsMap = this.metricsMap;
            reporter.baseTags = this.basicTags;
            return reporter;
        }
    }
}

